package vcp.client.GUI.mvc.view;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Iterator;

import javax.swing.DefaultComboBoxModel;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFormattedTextField;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingConstants;

import org.joda.time.DateTime;

import vcp.client.GUI.mvc.view.addons.CardPanel;
import vcp.client.interfaces.ICommand;
import vcp.client.utility.Common;
import vcp.client.utility.Common.UserNotification;
import vcp.client.utility.UtilityHelper;
import vcp.entity.Lot;
import vcp.entity.utility.ParkingMatrix;
import vcp.enums.DocumentState;
import vcp.enums.Floor;
import vcp.enums.LotStatus;
import vcp.enums.PopUpMessageType;
import vcp.enums.SiteEnum;

/**
 * <p>
 * user interface site map view class
 * </p>
 * 
 * @author VCP team
 * 
 */
public class UISiteMapView extends UIAbstractView implements ItemListener
{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	private SiteEnum currentSiteID;

	private ParkingMatrix parkingMatrix;

	private final HashMap <JButton, Lot> swingToLotMap;

	private final EnumMap <Floor, ArrayList <SimpleEntry <JButton, Lot>>> resetFloorsMap;
	private ArrayList <Floor> floorsForUndo;

	private JComboBox <LotStatus> cbEditStatuses;
	private JComboBox <Floor> cbFloors;
	private JComboBox <SiteEnum> cbSites;

	private JButton btnSubmit;
	private JButton btnEdit;
	private JButton btnUndo;
	private JLabel lblFloor;

	private boolean isEditMode = false;
	private DocumentState documentState;

	private CardPanel cardsPanel;
	private JPanel lowerMenuPanel;
	private JPanel panel;
	private JPanel panel_1;
	private JPanel panel_2;
	private JPanel panel_3;
	private JLabel lblSite;
	private JPanel pSaveAs;
	private JButton btnSave;

	private ICommand commandExecutor;

	/**
	 * setter
	 * 
	 * @param commandExecutor
	 */
	public void setCommandExecutor ( ICommand commandExecutor )
	{
		this.commandExecutor = commandExecutor;
	}

	/**
	 * constructor
	 */
	public UISiteMapView ()
	{
		super();
		this.swingToLotMap = new HashMap <>();
		this.resetFloorsMap = new EnumMap <>(Floor.class);

		for ( Floor floor : Floor.values() )
		{
			this.resetFloorsMap.put(floor, new ArrayList <SimpleEntry <JButton, Lot>>());
		}
	}

	/****************************************************************************************************************************
	 *** Public methods ***
	 ****************************************************************************************************************************/
	/**
	 * getter
	 * 
	 * @return current site id
	 */
	public SiteEnum getCurrentSiteEnum ()
	{
		return this.currentSiteID;
	}

	/**
	 * method enables site options
	 * 
	 * @param enable
	 */
	public void setSitesOptionEnabled ( boolean enable )
	{
		this.cbSites.setEnabled(enable);
	}

	/**
	 * method enables editing site statuses
	 * 
	 * @param enable
	 */
	public void setEditableStatusesEnabled ( boolean enable )
	{
		this.cbEditStatuses.setEnabled(enable);
	}

	/**
	 * method loads site options
	 * 
	 * @param values
	 */
	public void loadSitesOptions ( SiteEnum[] values )
	{
		this.cbSites.setModel(new DefaultComboBoxModel <>(values));
		this.cbSites.setMaximumRowCount(values.length);
	}

	/**
	 * method loads lot options for changing lot status
	 * 
	 * @param values
	 */
	public void loadEditableStatusesOptions ( LotStatus[] values )
	{
		this.cbEditStatuses.setModel(new DefaultComboBoxModel <>(values));
		this.cbEditStatuses.setMaximumRowCount(values.length);
	}

	/**
	 * method displays lower menu panel
	 * 
	 * @param show
	 */
	public void showLowerMenuPanel ( boolean show )
	{
		this.lowerMenuPanel.setVisible(show);
	}

	/**
	 * method displays save file to pdf button
	 * 
	 * @param canSaveToPdf
	 */
	public void showSaveButton ( boolean canSaveToPdf )
	{
		this.pSaveAs.setVisible(canSaveToPdf);
	}

	/**
	 * method loads and displays parking lots
	 * 
	 * @param matrix
	 */
	public void loadParkingMatrix ( ParkingMatrix matrix )
	{
		this.parkingMatrix = matrix;
		this.currentSiteID = this.parkingMatrix.getParentSite().getSite();
		this.documentState = DocumentState.NOT_EDITED;

		for ( int i = 0; i < this.parkingMatrix.getNumberOfFloor(); i ++ )
		{
			int rows = this.parkingMatrix.getNumberOfRows();
			int columns = this.parkingMatrix.getNumberOfColumns();
			Floor floor = Floor.convert(String.valueOf(i + 1));

			this.cardsPanel.addPanel(rows, columns, floor);
		}

		Iterator <Lot> iterator = this.parkingMatrix.iterator();
		int floorNumber = 1;
		while ( iterator.hasNext() )
		{
			Lot lot = iterator.next();
			if ( lot.getFloor() != floorNumber )
			{
				floorNumber ++ ;
			}

			JButton lotButton = new JButton(this.lotStatusToString(lot));
			lotButton.setName(Common.BTN_LOT);
			lotButton.setBackground(lot.getStatusColor());
			lotButton.addActionListener(this);

			Lot lotCopy = lot.copyLot();
			this.swingToLotMap.put(lotButton, lotCopy);

			SimpleEntry <JButton, Lot> simpleEntry = new SimpleEntry <>(lotButton, lot);
			Floor floor = Floor.convert(String.valueOf(lotCopy.getFloor()));
			this.resetFloorsMap.get(floor).add(simpleEntry);

			this.cardsPanel.addComponent(lotButton);
		}

		this.add(this.cardsPanel, BorderLayout.CENTER);
	}

	/****************************************************************************************************************************
	 *** Private methods ***
	 ****************************************************************************************************************************/
	/**
	 * method executes pressing save button
	 */
	private void onBtnSave ()
	{
		if ( this.commandExecutor.canExecute() )
		{
			this.commandExecutor.execute();
		}
		else
		{
			this.controllerDelegate.showPopUpMessage(UserNotification.CANNOT_SAVE_TO_PDF, PopUpMessageType.WARNING);
		}
	}

	/**
	 * method executes pressing on any lot on screen
	 * 
	 * @param siteMapButton
	 */
	private void onBtnLot ( JButton siteMapButton )
	{
		if ( this.isEditMode )
		{
			LotStatus lotStatus = (LotStatus) this.cbEditStatuses.getSelectedItem();
			if ( this.isLotStatusChangeValid(siteMapButton, lotStatus) )
			{
				this.documentState = DocumentState.EDITED;
				if ( lotStatus == LotStatus.RESERVE )
				{
					JFormattedTextField ftfSubscription = new JFormattedTextField();

					Object[] param = new Object[]
						{ new JLabel("Pls. provide car id:"),
								ftfSubscription };

					int result = JOptionPane.showConfirmDialog(this, param, null, JOptionPane.CANCEL_OPTION);
					if ( result == JOptionPane.OK_OPTION )
					{
						try
						{
							Integer reservedCarID = Integer.parseInt(ftfSubscription.getText());
							Lot selectedLot = this.swingToLotMap.get(siteMapButton);
							selectedLot.setChanged(true);

							selectedLot.setCarID(reservedCarID);
							selectedLot.setLotStatus(lotStatus);
							siteMapButton.setText(this.lotStatusToString(selectedLot));
							siteMapButton.setBackground(lotStatus.getStateColor());
						}
						catch ( NumberFormatException e )
						{
							JOptionPane.showMessageDialog(this, UserNotification.WRONG_DATA);
						}
					}
				}
				else
				{
					Lot selectedLot = this.swingToLotMap.get(siteMapButton);
					if ( lotStatus == LotStatus.NON_WORKING )
					{
						DateTime dateTime = this.controllerDelegate.requestCurrentClientDateTime();
						selectedLot.setBrokeStartDate(dateTime);
					}
					selectedLot.setLotStatus(lotStatus);
					selectedLot.setChanged(true);

					siteMapButton.setBackground(lotStatus.getStateColor());
					siteMapButton.setText(this.lotStatusToString(selectedLot));
				}
			}
			else
			{
				String stateChangeWarning = String.format(UserNotification.SITE_MAP_CANNOT__StatusA__A__StatusB__LOT, lotStatus.toString(), LotStatus.convert(siteMapButton.getBackground()).toString());
				this.controllerDelegate.showPopUpMessage(stateChangeWarning, PopUpMessageType.WARNING);
			}
		}
	}

	/**
	 * method executes pressing undo batton
	 */
	private void onBtnUndo ()
	{
		ArrayList <Object> params = new ArrayList <>();
		params.add(UserNotification.FLOOR_RESET_CONFIRMATION);

		for ( Floor floor : Floor.values() )
		{
			JCheckBox cbFloor = new JCheckBox(floor.toString());
			cbFloor.setName(floor.toString());
			cbFloor.setSelected(true);
			cbFloor.addItemListener(this);

			params.add(cbFloor);
		}

		if ( this.documentState == DocumentState.EDITED )
		{
			this.floorsForUndo = new ArrayList <>(Arrays.asList(Floor.values()));
			int result = JOptionPane.showConfirmDialog(this, params.toArray(), null, JOptionPane.OK_CANCEL_OPTION);
			if ( result != JOptionPane.CANCEL_OPTION )
			{
				this.undoSiteToPreviousSavedState();
			}
		}
		params = null;
		this.floorsForUndo = null;
		this.documentState = DocumentState.NOT_EDITED;
	}

	/**
	 * method executes submit button press
	 */
	private void onBtnSubmit ()
	{
		if ( this.controllerDelegate != null )
		{
			this.saveChanges();
			this.controllerDelegate.onButtonPress(this.parkingMatrix.getParentSite());
			this.documentState = DocumentState.NOT_EDITED;
		}
	}

	/**
	 * method updates manager buttons
	 */
	private void updateManageButtons ()
	{
		this.btnSubmit.setEnabled(this.documentState == DocumentState.EDITED);
		this.btnUndo.setEnabled(this.isEditMode);
		this.cbEditStatuses.setEnabled(this.isEditMode);
		this.btnEdit.setEnabled( ! this.isEditMode);
	}

	/**
	 * method performs saving changes
	 */
	private void saveChanges ()
	{
		this.parkingMatrix.clear();
		for ( Lot lot : this.swingToLotMap.values() )
		{
			this.parkingMatrix.addLot(lot);
		}
	}

	/**
	 * convert lot status to string
	 * 
	 * @param lot
	 * @return lot status as string
	 */
	private String lotStatusToString ( Lot lot )
	{
		String status = "<html>" + lot.getStatusName();
		switch ( lot.getLotStatus() )
		{
			case FREE:
			case NON_WORKING:
				break;
			case OCCUPIED:
			case RESERVE:
				status = status + "<br><center>" + lot.getCarID() + "</center>";
				break;

		}

		return status + "</html>";
	}

	/**
	 * method rolls back to last saved site map
	 */
	private void undoSiteToPreviousSavedState ()
	{
		for ( Floor undoFloor : this.floorsForUndo )
		{
			Iterator <SimpleEntry <JButton, Lot>> iterator = this.resetFloorsMap.get(undoFloor).iterator();
			while ( iterator.hasNext() )
			{
				SimpleEntry <JButton, Lot> resetEntry = iterator.next();
				JButton resetButton = resetEntry.getKey();
				Lot resetLot = resetEntry.getValue();

				resetButton.setBackground(resetLot.getStatusColor());
				resetButton.setText(this.lotStatusToString(resetLot));
				this.swingToLotMap.get(resetButton).setLotStatus(resetLot.getLotStatus());
			}
		}
	}

	/**
	 * checks if changing lot status is valid
	 * 
	 * @param siteMapButton
	 * @param lotStatus
	 * @return true if valid
	 */
	private boolean isLotStatusChangeValid ( JButton siteMapButton, LotStatus lotStatus )
	{
		return ( siteMapButton.getBackground() == Color.GREEN ) || ( ( siteMapButton.getBackground() == Color.RED ) && ( LotStatus.FREE == lotStatus ) );
	}

	/****************************************************************************************************************************
	 *** UIAbstractView methods ***
	 ****************************************************************************************************************************/
	@Override
	public void clearView ()
	{
		this.currentSiteID = null;
		this.parkingMatrix = null;
		this.floorsForUndo = null;

		this.isEditMode = false;
		this.documentState = DocumentState.NOT_EDITED;
		this.updateManageButtons();

		this.btnSubmit.setEnabled(false);

		for ( ArrayList <SimpleEntry <JButton, Lot>> entryList : this.resetFloorsMap.values() )
		{
			entryList.clear();
		}

		this.swingToLotMap.clear();
		this.cardsPanel.clear();

		this.cbFloors.setSelectedIndex(0);
	}

	@Override
	protected void setupLayout ()
	{
		this.cardsPanel = new CardPanel();

		this.setPreferredSize(new Dimension(904, 708));
		this.setLayout(new BorderLayout(0, 0));

		JPanel upperMenuPanel = new JPanel();
		upperMenuPanel.setPreferredSize(new Dimension(10, 60));
		this.add(upperMenuPanel, BorderLayout.NORTH);
		upperMenuPanel.setLayout(new GridLayout(0, 4, 0, 0));

		this.panel_1 = new JPanel();
		FlowLayout flowLayout = (FlowLayout) this.panel_1.getLayout();
		flowLayout.setAlignment(FlowLayout.LEFT);
		flowLayout.setVgap(15);
		upperMenuPanel.add(this.panel_1);

		this.cbEditStatuses = new JComboBox <>();
		this.panel_1.add(this.cbEditStatuses);
		this.cbEditStatuses.setFont(new Font("Lucida Grande", Font.PLAIN, 18));
		this.cbEditStatuses.setFocusable(false);
		this.cbEditStatuses.setEnabled(false);

		this.panel = new JPanel();
		FlowLayout flowLayout_1 = (FlowLayout) this.panel.getLayout();
		flowLayout_1.setAlignment(FlowLayout.LEFT);
		flowLayout_1.setVgap(15);
		upperMenuPanel.add(this.panel);

		this.lblFloor = new JLabel("Floor:");
		this.lblFloor.setFont(new Font("Lucida Grande", Font.PLAIN, 18));
		this.panel.add(this.lblFloor);
		this.lblFloor.setVerticalAlignment(SwingConstants.TOP);

		this.cbFloors = new JComboBox <>();
		this.panel.add(this.cbFloors);
		this.cbFloors.setFont(new Font("Lucida Grande", Font.PLAIN, 18));
		this.cbFloors.setFocusable(false);
		this.cbFloors.setModel(new DefaultComboBoxModel <Floor>(Floor.values()));

		this.panel_2 = new JPanel();
		FlowLayout flowLayout_3 = (FlowLayout) this.panel_2.getLayout();
		flowLayout_3.setAlignment(FlowLayout.RIGHT);
		flowLayout_3.setVgap(12);
		upperMenuPanel.add(this.panel_2);

		this.pSaveAs = new JPanel();
		this.panel_2.add(this.pSaveAs);

		this.btnSave = new JButton(Common.BTN_SAVE_AS);
		this.btnSave.setName(Common.BTN_SAVE_AS);
		this.btnSave.setFont(new Font("Tahoma", Font.PLAIN, 15));
		this.pSaveAs.add(this.btnSave);

		this.panel_3 = new JPanel();
		FlowLayout flowLayout_2 = (FlowLayout) this.panel_3.getLayout();
		flowLayout_2.setAlignment(FlowLayout.RIGHT);
		flowLayout_2.setVgap(15);
		upperMenuPanel.add(this.panel_3);

		this.lblSite = new JLabel("Site:");
		this.lblSite.setFont(new Font("Lucida Grande", Font.PLAIN, 18));
		this.panel_3.add(this.lblSite);

		this.cbSites = new JComboBox <>();
		this.panel_3.add(this.cbSites);
		this.cbSites.setFont(new Font("Lucida Grande", Font.PLAIN, 18));
		this.cbSites.setFocusable(false);

		this.lowerMenuPanel = new JPanel();
		this.lowerMenuPanel.setVisible(false);
		this.lowerMenuPanel.setPreferredSize(new Dimension(10, 60));
		this.add(this.lowerMenuPanel, BorderLayout.SOUTH);
		this.lowerMenuPanel.setLayout(new GridLayout(0, 6, 0, 0));

		this.btnUndo = new JButton(Common.BTN_UNDO);
		this.btnUndo.setName(Common.BTN_UNDO);
		this.btnUndo.setEnabled(false);
		this.lowerMenuPanel.add(this.btnUndo);

		JPanel spacePanel1 = new JPanel();
		this.lowerMenuPanel.add(spacePanel1);

		JPanel spacePanel2 = new JPanel();
		this.lowerMenuPanel.add(spacePanel2);

		JPanel spacePanel3 = new JPanel();
		this.lowerMenuPanel.add(spacePanel3);

		this.btnEdit = new JButton(Common.BTN_EDIT);
		this.btnEdit.setName(Common.BTN_EDIT);
		this.lowerMenuPanel.add(this.btnEdit);

		this.btnSubmit = new JButton(Common.BTN_SUBMIT);
		this.btnSubmit.setName(Common.BTN_SUBMIT);
		this.btnSubmit.setEnabled(false);
		this.lowerMenuPanel.add(this.btnSubmit);

	}

	@Override
	protected void setupListeners ()
	{
		this.btnEdit.addActionListener(this);
		this.btnUndo.addActionListener(this);
		this.btnSubmit.addActionListener(this);
		this.btnSave.addActionListener(this);

		this.cbFloors.addItemListener(this.cardsPanel);
		this.cbSites.addItemListener(this);
	}

	@Override
	protected void setupButtonsIcons ()
	{
		int iconTextGap = 20;

		ImageIcon icon = UtilityHelper.getUtilityHelperInstance().loadScaledImage(Common.ICON_SUBMIT, 40, 40);
		if ( icon != null )
		{
			this.btnSubmit.setIcon(icon);
			this.btnSubmit.setIconTextGap(iconTextGap);
		}

		icon = UtilityHelper.getUtilityHelperInstance().loadScaledImage(Common.ICON_GO_BACK, 40, 40);
		if ( icon != null )
		{
			this.btnUndo.setIcon(icon);
			this.btnUndo.setIconTextGap(iconTextGap);
		}

		icon = UtilityHelper.getUtilityHelperInstance().loadScaledImage(Common.ICON_EDIT, 40, 40);
		if ( icon != null )
		{
			this.btnEdit.setIcon(icon);
			this.btnEdit.setIconTextGap(iconTextGap);
		}
	}

	@Override
	public void actionPerformed ( ActionEvent actionEvent )
	{
		JButton siteMapButton = (JButton) actionEvent.getSource();
		switch ( siteMapButton.getName() )
		{
			case Common.BTN_SUBMIT:
				this.onBtnSubmit();
				break;
			case Common.BTN_UNDO:
				this.onBtnUndo();
				break;

			case Common.BTN_LOT:
				this.onBtnLot(siteMapButton);
				break;
			case Common.BTN_SAVE_AS:
				this.onBtnSave();
				return;
		}

		if ( ! siteMapButton.getName().equalsIgnoreCase(Common.BTN_LOT) )
		{
			this.isEditMode = ! this.isEditMode;
		}

		this.updateManageButtons();
		siteMapButton = null;
	}

	@Override
	public void itemStateChanged ( ItemEvent e )
	{
		if ( e.getSource() instanceof JCheckBox )
		{
			JCheckBox itemStateChangeCheckBox = (JCheckBox) e.getSource();
			Floor floor = Floor.convert(itemStateChangeCheckBox.getName());
			if ( e.getStateChange() == ItemEvent.DESELECTED )
			{
				this.floorsForUndo.remove(floor);
			}
			else if ( e.getStateChange() == ItemEvent.SELECTED )
			{
				this.floorsForUndo.add(floor);
			}
		}
		else
		{
			if ( e.getStateChange() == ItemEvent.SELECTED )
			{
				if ( this.controllerDelegate != null )
				{
					SiteEnum selectedSiteID = (SiteEnum) this.cbSites.getSelectedItem();
					if ( selectedSiteID != this.currentSiteID )
					{
						this.currentSiteID = selectedSiteID;
						this.controllerDelegate.onButtonPress(null);
					}
				}
			}
		}
	}

}
